/*
 * Copyright 2006 Matt Jensen
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package org.spectaql.internal.metadata;

import org.spectaql.metadata.EntityMetadata;
import org.spectaql.metadata.PropertyMetadata;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

/**
 * <p>
 * <code>EntityMetadataBuilder</code> is a builder object which is used to construct {@link EntityMetadata} instances.
 * </p>
 */
public class EntityMetadataBuilder
{
    /**
     * <p>
     * The entity class. 
     * </p>
     */
    private final Class<?> m_entityClass;

    /**
     * <p>
     * The entity name. 
     * </p>
     */
    private final String m_entityName;

    /**
     * <p>
     * The identifier class. 
     * </p>
     */
    private final Class<?> m_identifierClass;

    /**
     * <p>
     * The identifier property. 
     * </p>
     */
    private PropertyMetadata m_identifierProperty;

    /**
     * <p>
     * Property metadata. 
     * </p>
     */
    private final Set<PropertyMetadata> m_propertyMetadata = new HashSet<PropertyMetadata>();

    /**
     * <p>
     * Construct a {@link EntityMetadataBuilder} instance.
     * </p>
     *
     * @param entityName the entity name.
     * @param entityClass the entity class.
     * @param identifierClass the identifier class.
     */
    public EntityMetadataBuilder(final String entityName, final Class<?> entityClass, final Class<?> identifierClass)
    {
        super();
        assert null != entityClass : "The [entityClass] argument cannot be null.";
        assert null != entityName : "The [entityName] argument cannot be null.";
        assert null != identifierClass : "The [identifierClass] argument cannot be null.";

        m_entityClass = entityClass;
        m_entityName = entityName;
        m_identifierClass = identifierClass;
    }

    /**
     * <p>
     * Add a property to the entity metadata.
     * </p>
     *
     * @param name the property name.
     * @param clazz the property class.
     * @param nullable flag indicating whether the property is nullable.
     * @param readMethod the property accessor method.
     * @param writeMethod the property mutator method.
     * @param isIdentifierProperty flag indicating whether this is the entity identifier property.
     * @return {@link EntityMetadataBuilder} this instance.
     */
    public EntityMetadataBuilder addProperty(final String name,
                                             final Class<?> clazz,
                                             final boolean nullable,
                                             final Method readMethod,
                                             final Method writeMethod,
                                             final boolean isIdentifierProperty)
    {
        final PropertyMetadata propertyMetadata = new PropertyMetadataImpl(name, clazz, nullable, readMethod,
                writeMethod, isIdentifierProperty);
        m_propertyMetadata.add(propertyMetadata);
        if (isIdentifierProperty)
        {
            if (null != m_identifierProperty)
            {
                throw new IllegalStateException("Identifier property has already been set.");
            }
            m_identifierProperty = propertyMetadata;
        }
        return this;
    }

    /**
     * <p>
     * Build an {@link EntityMetadata} instance from the information which has been stored in this builder instance.
     * </p>
     *
     * @return {@link EntityMetadata} instance.
     */
    public final EntityMetadata toEntityMetadata()
    {
        return createEntityMetadata(m_entityName, m_entityClass, m_identifierClass, m_propertyMetadata);
    }

    /**
     * <p>
     * Create an {@link EntityMetadata} instance from given entity information.
     * </p>
     * 
     * @param entityName the entity name.
     * @param entityClass the entity class.
     * @param identifierClass the identifier class.
     * @param propertyMetadata the property metadata.
     * @return {@link EntityMetadata} instance.
     */
    protected EntityMetadata createEntityMetadata(final String entityName,
                                                  final Class<?> entityClass,
                                                  final Class<?> identifierClass,
                                                  final Set<PropertyMetadata> propertyMetadata)
    {
        return new EntityMetadataImpl(entityName, entityClass, identifierClass, propertyMetadata);
    }
}
